import { Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
import { Http } from '@capacitor-community/http';
import { Capacitor } from '@capacitor/core';
import { Filesystem, Directory } from '@capacitor/filesystem';
import { LoadingController, Platform } from '@ionic/angular';
import { NgEventBus } from 'ng-event-bus';
import { Sentence } from 'src/app/model/sentence';
import { UserInfoService } from 'src/app/service/user.info.service';
import { retry } from 'ts-retry-promise';
import { ReadingService } from '../../services/reading.service';

@Component({
  selector: 'app-player',
  templateUrl: './player.component.html',
  styleUrls: ['./player.component.scss'],
})
export class PlayerComponent implements OnInit, OnDestroy, OnChanges {
  @ViewChild('audio', { static: true })
  public audio!: ElementRef;

  @Input()
  public waitTime: number;
  @Input()
  public autoPlaying: boolean;
  @Input()
  public sentence: Sentence;
  @Input()
  public currentSequenceNo: number;

  @Output()
  public gotoNextChapter = new EventEmitter();
  @Output()
  public gotoPreviousChapter = new EventEmitter();
  @Output()
  public loadNextSentence = new EventEmitter<number>();

  public position: any = 0;
  public isReady = false;
  public isInPlay = false;
  public playing = false;

  private audioPromise: any;
  private durationValue: number;
  private pauseBySwipe: boolean;
  private playCount = 0;

  public constructor(private readingService: ReadingService, private loadingCtrl: LoadingController, private platform: Platform) {}

  public get duration() {
    if (!this.sentence) {
      this.durationValue = 0;
    } else {
      this.durationValue = this.sentence.duration / 1000;
    }
    return this.durationValue;
  }
  public set duration(value) {
    this.durationValue = value;
  }

  public async ngOnChanges(changes: SimpleChanges) {
    if (changes.sentence && changes.sentence.currentValue !== changes.sentence.previousValue) {
      const sentence = changes.sentence.currentValue;
      if (sentence) {
        const fileUrl = await this.storeFileToLocal(sentence);
        this.audio.nativeElement.src = fileUrl;
        this.load();
      }
    }
  }

  public ngOnInit() {
    this.audio.nativeElement.addEventListener('playing', () => this.setStatusWhenPlaying());
    this.audio.nativeElement.addEventListener('pause', () => this.setStatusWhenPause());
    this.audio.nativeElement.addEventListener('waiting', () => this.setStatusWhenPause());
    this.audio.nativeElement.addEventListener('timeupdate', () => this.setStatusTimeupdate());
    this.audio.nativeElement.addEventListener('loadstart', () => {
      this.setStatusLoadData();
    });
    this.audio.nativeElement.addEventListener('loadeddata', () => {
      if (this.autoPlaying && this.playCount !== 0 && !this.pauseBySwipe) {
        this.play();
      }
    });
    this.audio.nativeElement.addEventListener('ended', () => {
      this.setStatusEnded();
    });

    this.readingService.triggerSourceSubject.subscribe((source) => {
      if (source === 'slider') {
        this.pause();
        this.pauseBySwipe = true;
      }
    });
  }

  public ngOnDestroy(): void {
    this.audio.nativeElement.removeEventListener('playing', () => this.setStatusWhenPlaying());
    this.audio.nativeElement.removeEventListener('pause', () => this.setStatusWhenPause());
    this.audio.nativeElement.removeEventListener('waiting', () => this.setStatusWhenPause());
    this.audio.nativeElement.removeEventListener('timeupdate', () => this.setStatusTimeupdate());
    this.audio.nativeElement.removeEventListener('loadstart', () => {
      this.setStatusLoadData();
    });
    this.audio.nativeElement.removeEventListener('loadeddata', () => {
      this.setStatusLoadData();
    });
    this.audio.nativeElement.removeEventListener('ended', () => this.setStatusEnded());
  }
  public play() {
    this.audioPromise = this.audio.nativeElement.play();
    this.pauseBySwipe = false;
    this.playCount++;
  }
  public load() {
    this.audio.nativeElement.load();
  }
  public pause() {
    this.audioPromise
      ?.then((_) => {
        this.audio.nativeElement.pause();
      })
      .catch((error) => {});
  }

  public changePosition(position) {
    this.audio.nativeElement.currentTime = position;
  }
  public controlSeconds(action: string) {
    const step = 15;
    const num = this.position;
    switch (action) {
      case 'back':
        this.position = num < step ? 0.001 : num - step;
        this.audio.nativeElement.currentTime = this.position;
        break;
      case 'forward':
        this.position = num + step < this.duration ? num + step : this.duration;
        this.audio.nativeElement.currentTime = this.position;
        break;
      default:
        break;
    }
  }
  public goto(direction: string) {
    if (direction === 'back') {
      this.gotoPreviousChapter.emit();
    } else {
      this.gotoNextChapter.emit();
    }
  }

  private setStatusEnded() {
    this.isInPlay = false;
    this.playing = false;
    this.readingService.isPlayingSubject.next(this.playing);

    if (this.autoPlaying) {
      this.readingService.setTriggerSource('player');
      this.isReady = false;
      this.sleep(this.waitTime).then(() => {
        if (this.readingService.triggerSource === 'player') {
          this.loadNextSentence.emit(this.currentSequenceNo);
          this.isReady = true;
        }
      });
    }
  }

  private setStatusLoadData() {
    this.isReady = true;
    this.isInPlay = false;
    this.playing = false;
    this.audio.nativeElement.currentTime = 0;
    this.readingService.isPlayingSubject.next(this.playing);
  }

  private setStatusTimeupdate() {
    this.position = this.audio.nativeElement.currentTime;
  }

  private setStatusWhenPause() {
    this.playing = false;
    this.isInPlay = true;
    this.readingService.isPlayingSubject.next(this.playing);
  }

  private setStatusWhenPlaying() {
    this.playing = true;
    this.isInPlay = true;
    this.readingService.isPlayingSubject.next(this.playing);
  }
  private sleep(time) {
    return new Promise((resolve) => setTimeout(resolve, time * 1000));
  }

  private async storeFileToLocal(sentence: Sentence) {
    const platform = await this.platform.ready();
    if (platform === 'dom') {
      return sentence.fileUrl;
    }

    const url = sentence.fileUrl;
    const p = url.lastIndexOf('/');
    const fileName = url.substring(p + 1);
    let readFileResult;
    try {
      readFileResult = await Filesystem.readFile({
        path: fileName,
        directory: Directory.Cache,
      });
    } catch (err) {
      console.log(err);
    }
    console.log('File exist', !!readFileResult);
    let fileUrl = sentence.fileUrl;
    try {
      if (!readFileResult) {
        const options = {
          url: sentence.fileUrl,
          filePath: fileName,
          fileDirectory: Directory.Cache,
        };
        await retry(
          async () => {
            const response = await Http.downloadFile(options);
            console.log('File downloaded path', response.path);
            fileUrl = Capacitor.convertFileSrc(response.path);
            console.log('File converted Url', fileUrl);
          },
          { retries: 0 }
        );
      } else {
        const urlResult = await Filesystem.getUri({
          path: fileName,
          directory: Directory.Cache,
        });
        fileUrl = Capacitor.convertFileSrc(urlResult.uri);
        console.log('File get Url', fileUrl);
      }
    } catch (err) {
      console.log(err);
    }
    return fileUrl;
  }
}

